package robocup.component;

import java.io.IOException;
import java.io.StringReader;
import java.util.Properties;

import robocup.component.actions.InitAction;
import robocup.component.coach.CoachFactBase;
import robocup.component.coach.CoachInfoReceiver;
import robocup.component.coach.CoachWorld;
import robocup.component.coach.OLCoachTactic;
import robocup.component.infotypes.AuralInfo;
import robocup.component.infotypes.CoachVisualInfo;
import robocup.component.infotypes.Info;
import robocup.component.infotypes.PlayerTypesInfo;
import robocup.component.infotypes.ServerParamInfo;
import robocup.parser9.CoachParser;
import robocup.parser9.ParseException;

/**
 * This class is the implementation of a coach, and therefore extends the
 * <code>AbstractAgent</code>. Additional components are the <code>CoachWorld
 * </code>,
 * which is the Coaches world-model, and the <code>InfoReceiver</code>, which
 * is the tactical interface for the coach.
 */
public class CoachAgent
    extends AbstractAgent {

  /**
   * The knowledge of the coach about the game state. It is noise-free and
   * always complete regarding information about players or the ball.
   */
  private CoachWorld   coachWorld;

  /**
   * The tactical interface is actually the <code>OLCoachTactic</code>. This
   * includes a set of <code>InfoReceiver</code>, which are able to register
   * for certain information (visual, acustic, etc.), analyze them and react via
   * CLang in a defined manner.
   */
  private CoachInfoReceiver tactic;

  /**
   * The Coach-Agent constructor only forwards the properties of the agent to
   * the super-class.
   * 
   * @param props -
   *          the agent properties
   */
  public CoachAgent(
      final Properties props) {

    super(props);
  }

  /**
   * This method initializes additional agent properties. The Coach has none!
   */
  @Override
  protected void initConfig() {

  }

  /**
   * The components of the coach are the CoachWorld, the CoachTactic and the
   * CoachParser resp.
   */
  @Override
  protected void initComponents() {

    this.coachWorld = new CoachWorld(new CoachFactBase());
    this.coachWorld.setParent(this);
    this.parser = new CoachParser(new StringReader(""), this);
    this.tactic = new OLCoachTactic(this);
  }

  /**
   * The startup protocol of the coachagent is very simple. After connection, it
   * sends the eye and ear on commands, such that it receives all information
   * properly. Switching these off might cause problems, since the Thread of
   * this agent terminates due to timeouts.
   */
  @Override
  protected boolean initProtocol() {

    try {
      // first connect
      final String init = new InitAction(AbstractAgent.COACH, this.teamName)
          .toString();
      this.connection.send(init);

      Info info = null;
      String rcv = null;

      rcv = this.connection.receive();
      if (rcv == null) {
        System.out.println("Error: Server does not respond in time!");
        return false;
      }

      // send eye on (ear command only for trainer):
      this.connection.send("(eye on)");
      this.connection.receive();

      info = this.parser.parseCommand(rcv);

      System.out.println("[CoachAgent] Coach " + " successfully connected! ");

      // trigger updates
      this.informWorld(info);
      this.informTactic(info);
    }
    catch (final IOException e) {
      e.printStackTrace();
      return false;
    }
    catch (final ParseException p) {
      p.printStackTrace();
      return false;
    }
    return true;
  }

  /**
   * Information is forwarded to the CoachWorld first, and then to the tactic.
   * No synchronization is necessary.
   */
  @Override
  protected void processInfo(final Info info) {
    this.informWorld(info);
    this.informTactic(info);
  }

  /**
   * This method delegates the updates of the worldmodel by analyzing the given
   * info-type.
   * 
   * @param info -
   *          a generic info
   */
  private void informWorld(final Info info) {

    if (info == null) {
      return;
    }
    if (info.getInfoType() == Info.TYPE_COACHVI) {
      this.coachWorld.update((CoachVisualInfo) info);
    }
    else if (info.getInfoType() == Info.TYPE_AURAL) {
      this.coachWorld.update((AuralInfo) info);
    }
    else if (info.getInfoType() == Info.TYPE_SPARAM) {
      this.coachWorld.setSConf(((ServerParamInfo) info).getSConf());
    }
  }

  /**
   * This method calls the tactic of the coach dependent on the info-type.
   * 
   * @param info -
   *          the recevied info!
   */
  private void informTactic(final Info info) {

    if (info == null) {
      return;
    }
    if (info.getInfoType() == Info.TYPE_PTYPE) {
      this.tactic.update((PlayerTypesInfo) info);
    }
    if (info.getInfoType() == Info.TYPE_AURAL) {
      this.tactic.update((AuralInfo) info);
    }
    if (info.getInfoType() == Info.TYPE_COACHVI) {
      this.tactic.update((CoachVisualInfo) info);
    }
  }

  /**
   * The getter for the <code>CoachWorld</code>
   * 
   * @return retrieves the coachworld!
   */
  public CoachWorld getCoachWorld() {

    return this.coachWorld;
  }

}
